1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
use crate::co;
use crate::decl::*;
use crate::guard::*;
use crate::prelude::*;
/// Access types for [`File::open`](crate::File::open) and
/// [`FileMapped::open`](crate::FileMapped::open).
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum FileAccess {
/// Opens the file as read-only. Fails if the file doesn't exist.
ExistingReadOnly,
/// Opens the file as read/write. Fails if the file doesn't exist.
ExistingRW,
/// Opens the file as read/write. If the file doesn't exist, it will be
/// created.
OpenOrCreateRW,
/// Creates a new file as read/write. Fails if the file already exists.
CreateRW,
}
impl std::fmt::Display for FileAccess {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
FileAccess::ExistingReadOnly => "Existing file, read-only",
FileAccess::ExistingRW => "Existing file, read and write",
FileAccess::OpenOrCreateRW => "Open existing file or create new file, read and write",
FileAccess::CreateRW => "Create new file, read and write",
})
}
}
//------------------------------------------------------------------------------
/// Manages an [`HFILE`](crate::HFILE) handle, which provides file read/write
/// and other operations. It is closed automatically when the object goes out of
/// scope.
///
/// This is an alternative to the standard [`std::fs::File`], with a possibly
/// faster implementation since it's Windows-only.
///
/// If you just want to read the file, consider memory-mapping it with
/// [`FileMapped`](crate::FileMapped), which tends to be faster.
///
/// # Examples
///
/// Reading the contents as a string:
///
/// ```no_run
/// use winsafe::{self as w, prelude::*};
///
/// let f = w::File::open(
/// "C:\\Temp\\foo.txt",
/// w::FileAccess::ExistingRW,
/// )?;
/// let raw_bytes = f.read_all()?;
/// let text = w::WString::parse(&raw_bytes)?.to_string();
/// # w::SysResult::Ok(())
/// ```
///
/// Erasing the file and writing a string:
///
/// ```no_run
/// use winsafe::{self as w, prelude::*};
///
/// let f = w::File::open(
/// "C:\\Temp\\foo.txt",
/// w::FileAccess::OpenOrCreateRW,
/// )?;
/// f.set_size(0)?; // truncate
/// f.write("My text".as_bytes())?;
/// # w::SysResult::Ok(())
/// ```
pub struct File {
hfile: CloseHandleGuard<HFILE>,
}
impl File {
/// Opens a file with the desired access.
#[must_use]
pub fn open(file_path: &str, access: FileAccess) -> SysResult<Self> {
let (acc, share, disp) = match access {
FileAccess::ExistingReadOnly => (
co::GENERIC::READ,
Some(co::FILE_SHARE::READ),
co::DISPOSITION::OPEN_EXISTING,
),
FileAccess::ExistingRW => (
co::GENERIC::READ | co::GENERIC::WRITE,
None,
co::DISPOSITION::OPEN_EXISTING,
),
FileAccess::OpenOrCreateRW => (
co::GENERIC::READ | co::GENERIC::WRITE,
None,
co::DISPOSITION::OPEN_ALWAYS,
),
FileAccess::CreateRW => (
co::GENERIC::READ | co::GENERIC::WRITE,
None,
co::DISPOSITION::CREATE_NEW,
),
};
let (hfile, _) = HFILE::CreateFile(
file_path, acc, share, None, disp,
co::FILE_ATTRIBUTE::NORMAL, None, None, None)?;
Ok(Self { hfile })
}
/// Returns the underlying file handle.
#[must_use]
pub fn hfile(&self) -> &HFILE {
&*self.hfile
}
/// Returns the position of the file pointer by calling
/// [`HFILE::SetFilePointerEx`](crate::prelude::kernel_Hfile::SetFilePointerEx).
#[must_use]
pub fn pointer_offset(&self) -> SysResult<u64> {
self.hfile.SetFilePointerEx(0, co::FILE_STARTING_POINT::CURRENT) // https://stackoverflow.com/a/17707021/6923555
.map(|off| off as _)
}
/// Calls [`HFILE::ReadFile`](crate::prelude::kernel_Hfile::ReadFile) to
/// read at most `buffer.len()` bytes from the file, starting at the current
/// file pointer offset. Returns how many bytes were actually read. The file
/// pointer is then incremented by the number of bytes read.
///
/// Note that the API limits the reading up to 4 GB.
pub fn read(&self, buffer: &mut [u8]) -> SysResult<u32> {
self.hfile.ReadFile(buffer)
}
/// Returns the size of the file by calling
/// [`HFILE::GetFileSizeEx`](crate::prelude::kernel_Hfile::GetFileSizeEx),
/// allocates the `Vec` buffer, then reads all the file bytes by calling
/// [`HFILE::ReadFile`](crate::prelude::kernel_Hfile::ReadFile).
///
/// Note that the API limits the reading up to 4 GB.
#[must_use]
pub fn read_all(&self) -> SysResult<Vec<u8>> {
self.set_pointer_offset(0)?;
let mut buf = vec![0x00; self.size()? as _];
self.read(&mut buf)?;
Ok(buf)
}
/// Sets the position of the file pointer by calling
/// [`HFILE::SetFilePointerEx`](crate::prelude::kernel_Hfile::SetFilePointerEx).
pub fn set_pointer_offset(&self, offset: u64) -> SysResult<()> {
self.hfile.SetFilePointerEx(offset as _, co::FILE_STARTING_POINT::BEGIN)
.map(|_| ())
}
/// Truncates or expands the file by calling
/// [`HFILE::SetFilePointerEx`](crate::prelude::kernel_Hfile::SetFilePointerEx)
/// and
/// [`HFILE::SetEndOfFile`](crate::prelude::kernel_Hfile::SetEndOfFile),
/// then sets the file pointer to the beginning of the file.
///
/// If the size is increased, the contents in the new area are undefined.
pub fn set_size(&self, num_bytes: u64) -> SysResult<()> {
self.set_pointer_offset(num_bytes)?;
self.hfile.SetEndOfFile()?;
self.set_pointer_offset(0)
}
/// Returns the size of the file by calling
/// [`HFILE::GetFileSizeEx`](crate::prelude::kernel_Hfile::GetFileSizeEx).
#[must_use]
pub fn size(&self) -> SysResult<u64> {
self.hfile.GetFileSizeEx()
}
/// Returns the creation and last write times of the file, in the current
/// time zone.
#[must_use]
pub fn times(&self) -> SysResult<(SYSTEMTIME, SYSTEMTIME)> {
let (mut ft_creation, mut ft_last_write) = (FILETIME::default(), FILETIME::default());
self.hfile.GetFileTime(Some(&mut ft_creation), None, Some(&mut ft_last_write))?;
let st_creation_utc = FileTimeToSystemTime(&ft_creation)?;
let st_last_write_utc = FileTimeToSystemTime(&ft_last_write)?;
let st_creation_local = SystemTimeToTzSpecificLocalTime(None, &st_creation_utc)?;
let st_last_write_local = SystemTimeToTzSpecificLocalTime(None, &st_last_write_utc)?;
Ok((st_creation_local, st_last_write_local))
}
/// Writes the bytes at the current file pointer by calling
/// [`HFILE::WriteFile`](crate::prelude::kernel_Hfile::WriteFile).
///
/// This method will fail if the file was opened with
/// [`FileAccess::ExistingReadOnly`](crate::FileAccess::ExistingReadOnly).
pub fn write(&self, data: &[u8]) -> SysResult<()> {
self.hfile.WriteFile(data)
.map(|_| ())
}
}